package com.andova.component.net

import android.content.Context
import android.net.ConnectivityManager
import com.andova.*
import com.andova.ext.ad
import com.andova.ext.ae
import com.andova.ext.ai
import com.andova.ext.launchSuspend
import com.google.gson.JsonSyntaxException
import okhttp3.Callback
import okhttp3.OkHttpClient
import okhttp3.Request
import okio.BufferedSink
import okio.Okio
import retrofit2.Call
import retrofit2.Response
import java.io.File
import java.io.IOException
import java.lang.ref.WeakReference
import java.net.ConnectException
import java.net.NoRouteToHostException
import java.net.SocketTimeoutException
import java.net.UnknownHostException
import javax.net.ssl.SSLHandshakeException


class RetrofitSusCallback2<T : ResponseModel> : retrofit2.Callback<T>, NetworkSusCall2 {
    private var mCall: Call<T>? = null
    private var mCallback: NetworkSusCallback2<T>? = null
    private var mContext: WeakReference<Context>? = null
    override fun onResponse(call: Call<T>, response: Response<T>) = launchSuspend {
        try {
            mCallback?.response(mContext ?: return@launchSuspend, call, response)
            val body = response.body()
            if (!response.isSuccessful || body == null || !body.isSuccess) {
                mCallback?.error(mContext ?: return@launchSuspend, response)
                mCallback?.complete(mContext ?: return@launchSuspend, ERROR_URL2SERVER)
                return@launchSuspend
            }
            if (body.isEmpty) {
                mCallback?.complete(mContext ?: return@launchSuspend, RESPONSE_EMPTY)
                return@launchSuspend
            }
            mCallback?.success(mContext ?: return@launchSuspend, body)
            mCallback?.complete(mContext ?: return@launchSuspend, RESPONSE_SUCCESS)
        } catch (e: Exception) {
            onFailure(call, e)
        }
    }

    override fun onFailure(call: Call<T>?, throwable: Throwable) = launchSuspend {
        mCallback?.failure(mContext ?: return@launchSuspend, throwable, call)
        when (throwable) {
            is JsonSyntaxException -> mCallback?.complete(mContext ?: return@launchSuspend, ERROR_JSON_SYNTAX)
            is SocketTimeoutException -> mCallback?.complete(mContext ?: return@launchSuspend, ERROR_TIMEOUT)
            is ConnectException -> mCallback?.complete(mContext ?: return@launchSuspend, ERROR_CONNECT)
            is SSLHandshakeException -> mCallback?.complete(mContext ?: return@launchSuspend, ERROR_SSL_HANDSHAKE)
            is NoRouteToHostException -> mCallback?.complete(mContext ?: return@launchSuspend, ERROR_NO_ROUTE_TO_HOST)
            is UnknownHostException -> mCallback?.complete(mContext ?: return@launchSuspend, ERROR_UNKNOWN_HOST)
            is IllegalArgumentException -> mCallback?.complete(mContext ?: return@launchSuspend, ERROR_ILLEGAL_ARGUMENT)
            else -> mCallback?.complete(mContext ?: return@launchSuspend, ERROR_UNKNOWN)
        }
    }

    @Suppress("UNCHECKED_CAST")
    override fun request(context: WeakReference<Context>, callback: NetworkSusCallback2<out ResponseModel>?, call: Any?) {
        if (!isOpenNetwork(context.get())) {
            launchSuspend { callback?.complete(context, ERROR_NO_NETWORK) }
            return
        }
        mCall = call as? Call<T>
        mContext = context
        mCallback = callback as? NetworkSusCallback2<T>
        launchSuspend { mCallback?.prepare(context) }
        try {
            // todo fix ‘java.lang.OutOfMemoryError: pthread_create (1040KB stack) failed: Try again’
            mCall?.enqueue(this)
        } catch (ex: Exception) {
            onFailure(mCall, ex)
        }
    }

    override fun download(url: String, path: String, callback: (msg: Any) -> Unit) = download(url, File(path), callback)

    @Suppress("NULLABILITY_MISMATCH_BASED_ON_JAVA_ANNOTATIONS")
    override fun download(url: String, dest: File, callback: (msg: Any) -> Unit) {
        val startTime = System.currentTimeMillis()
        ad("ready download $url")
        val request: Request = Request.Builder().url(url).build()
        OkHttpClient().newCall(request).enqueue(object : Callback {
            override fun onFailure(call: okhttp3.Call, e: IOException) {
                ae("download failed: $e")
                callback(e)
            }

            override fun onResponse(call: okhttp3.Call, response: okhttp3.Response) {
                var bufferedSink: BufferedSink? = null
                try {
                    bufferedSink = Okio.buffer(Okio.sink(dest))
                    bufferedSink.writeAll(response.body()?.source())
                    bufferedSink.close()
                    ai("download success, it take ${System.currentTimeMillis() - startTime}ms")
                    callback(dest)
                } catch (e: Exception) {
                    ae("download exception: $e")
                    callback(e)
                } finally {
                    bufferedSink?.close()
                }
            }
        })
    }

    override fun version(): String = "2.4.0"

    /**
     * 判断是否有网络可用
     */
    private fun isOpenNetwork(cxt: Context?): Boolean {
        cxt ?: return false
        val cm = cxt.getSystemService(Context.CONNECTIVITY_SERVICE) as? ConnectivityManager
        return cm?.activeNetworkInfo != null && cm.activeNetworkInfo?.isAvailable ?: false
    }
}